package com.webmining.sherlock.admin;

import com.webmining.sherlock.crawler.Crawler;
import com.webmining.sherlock.crawler.Page;
import com.webmining.sherlock.crawler.Scope;
import com.webmining.sherlock.store.Store;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import javax.servlet.ServletContext;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.sql.SQLException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;

public class Manager {

    /**
     * Sets environment for crawling an specific page.
     */
    public static void setDefaultInitialPage(URL page, ServletContext context)
            throws ConfigurationException {
        Logger log = Logger.getLogger(Configuration.class);
        // Add the page host to the scope configuration file
        String scopeFilename = Configuration.getScopeConfigFilename();
        try {
            URL urlFile = Manager.class.getClassLoader().getResource(scopeFilename);
            File scopeFile = new File(urlFile.toURI());
            FileWriter outputFileReader = new FileWriter(scopeFile,true);
            PrintWriter outputStream = new PrintWriter(outputFileReader);

            if (scopeFile.exists()) {
                scopeFile.delete();
            }
            PrintWriter out = new PrintWriter(
                    new FileOutputStream(scopeFile));
            String pattern =
                    Scope.domainToPattern(page.getHost()).pattern();
            out.print("DOMAIN " + pattern + " ACCEPT");
            out.flush();
            out.close();
        } catch (FileNotFoundException fnfe) {
            log.error("unable to create scope config file " + scopeFilename,
                    fnfe);
            throw new ConfigurationException("unable to create scope " +
                    "config file " + scopeFilename, fnfe);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        // Add the page to the initial pages configuration file
        String initialPagesFilename = Configuration.getInitialPagesConfigFilename();
        try {
            URL urlFile = Manager.class.getClassLoader().getResource(initialPagesFilename);
            File initialPagesFile = new File(urlFile.toURI());
            FileWriter outputFileReader = new FileWriter(initialPagesFile,true);
            PrintWriter outputStream = new PrintWriter(outputFileReader);

            if (initialPagesFile.exists()) {
                initialPagesFile.delete();
            }
            PrintWriter out = new PrintWriter(
                    new FileOutputStream(initialPagesFile));
            out.print(page.toExternalForm());
            out.flush();
            out.close();
        } catch (FileNotFoundException fnfe) {
            log.error("unable to create initial pages config file " +
                    initialPagesFilename, fnfe);
            throw new ConfigurationException("unable to create initial " +
                    "pages config file " + initialPagesFilename, fnfe);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    public static void initScheduler() throws SchedulerException,
            ConfigurationException {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        scheduler.start();
        // schedule crawling if a previous scheduler configuration
        // exists
        try {
            String schedulerConfigFilename =
                    Configuration.getSchedulerConfigFilename();

            InputStream schedulerConfigFile = Manager.class.getClassLoader().getResourceAsStream(schedulerConfigFilename);
            if (schedulerConfigFile != null) {
                String update = Configuration.getProperty(
                        schedulerConfigFilename, "org.sherlock.scheduler.update");
                Manager.scheduleCrawling(Long.parseLong(update));
            }
        } catch (ConfigurationException ce) {
            Logger log = Logger.getLogger(Configuration.class);
            log.warn("Delaying scheduling since no configuration is " +
                    "setted up", ce);
        }
    }

    public static void shutdownScheduler() throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        if (scheduler != null) {
            scheduler.shutdown();
        }
    }

    /**
     * @param hours time between crawling executions in hours
     */
    public static void scheduleCrawling(long hours) throws SchedulerException,
            ConfigurationException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        if (scheduler == null) {
            Logger log = Logger.getLogger(Configuration.class);
            log.error("unable to get the scheduler");
            throw new SchedulerException("unable to get the scheduler");
        }
        // lookup if the crawler is already scheduled and remove it
        // for rescheduling
        try {
            scheduler.deleteJob("sherlock", Scheduler.DEFAULT_GROUP);
        } catch (SchedulerException se) {
            Logger log = Logger.getLogger(Configuration.class);
            log.debug("No previous sherlock job was found for rescheduling");
        }
        JobDetail sherlockJob = new JobDetail(
                "sherlock", Scheduler.DEFAULT_GROUP, Crawler.class);
        SimpleTrigger trigger = new SimpleTrigger(
                "sherlockTrigger", Scheduler.DEFAULT_GROUP, new Date(), null,
                SimpleTrigger.REPEAT_INDEFINITELY, hours * 60L * 60L * 1000L);
        scheduler.scheduleJob(sherlockJob, trigger);
    }

    public static void initStore() throws ConfigurationException,
            IOException, SQLException {
        // verify if the sherlock database objects are created
        Store.verifyObjects();
    }

    public static void shutdownStore() throws IOException, SQLException {
        // TODO: shutdown store if using internal database
    }

    public static void initEnvironment()
            throws ConfigurationException {
        boolean hasErrors = false;
        // initialize sherlock logging system

        Logger log = Logger.getLogger(Manager.class);
        // initialize scheduling
        try {
            initScheduler();
        } catch (SchedulerException se) {
            log.error("Unable to initialize scheduler for sherlock", se);
            hasErrors = true;
        }
        // initialize sherlock store
        try {
            initStore();
        } catch (IOException ioe) {
            log.error("Unable to initialize index store for sherlock", ioe);
            hasErrors = true;
        } catch (SQLException se) {
            log.error("Unable to initialize database store for sherlock", se);
            hasErrors = true;
        }
        if (!hasErrors) {
            log.info("Sherlock successfully initialized");
        }
    }

    public static void shutdownEnvironment() {
        boolean hasErrors = false;
        Logger log = Logger.getLogger(Manager.class);
        try {
            shutdownScheduler();
        } catch (SchedulerException se) {
            log.error("Unable to shutdown scheduler for sherlock", se);
            hasErrors = true;
        }
        try {
            shutdownStore();
        } catch (IOException ioe) {
            log.error("Unable to shutdown index for sherlock", ioe);
            hasErrors = true;
        } catch (SQLException sqle) {
            log.error("Unable to shutdown database store for sherlock", sqle);
            hasErrors = true;
        }
        if (!hasErrors) {
            log.info("Sherlock shutdown successfully");
        }
    }

    public static void loadInitialPages(Scope scope, Store store) throws ConfigurationException {
        try {
            String initPagesFilename = null;
            initPagesFilename = Configuration.getInitialPagesConfigFilename();
            BufferedReader initPagesFile =
                    new BufferedReader(new InputStreamReader(
                            Manager.class.getClassLoader().getResourceAsStream(initPagesFilename)));
            String addressLine;
            Page pagina = new Page();
            pagina.setStore(store);
            pagina.setScope(scope);
            while ((addressLine = initPagesFile.readLine()) != null) {
                addressLine = addressLine.trim();
                if (addressLine.length() > 0 && addressLine.charAt(0) != '#') {
                    // Record the new initial page adding a rule to the scope
                    pagina.recordLink(addressLine, true);
                }
            }
        } catch (FileNotFoundException fnfe) {
            Logger log = Logger.getLogger(Manager.class);
            log.error("Unable to load initial pages", fnfe);
            throw new ConfigurationException("Unable to load initial pages", fnfe);
        } catch (IOException ioe) {
            Logger log = Logger.getLogger(Manager.class);
            log.error("Unable to load initial pages", ioe);
            throw new ConfigurationException("Unable to load initial pages", ioe);
        }
    }

}
